#!/bin/python3
import time

import wiringpi


class IR:
    """
    红外收发类
    """

    # 信号时序
    StartLevel = (9000, 4500)  # 起始码
    LinkLevel = (560, 20000)  # 连接码
    
    HighLevel = (560, 1680)  # 高电平
    LowLevel = (560, 560)  # 低电平

    def __init__(self):
        self.input_pin = 0
        self.is_start = False
        
        self.before_last_time = 0
        self.last_time = 0
        self.data_list = []

        wiringpi.wiringPiSetup()
        # 用于初始化 GPIO（通用输入输出）的函数，它会初始化并返回一个文件描述符，用于控制 GPIO 端口。

    @staticmethod
    def _is_equal(time0: int, time1: int) -> bool:
        return abs(time0 - time1) < 500
    # 比较两个时间戳（以微秒为单位）是否相等，如果两个时间戳之差小于 500 微秒，返回 True，否则返回 False。

    @staticmethod
    def _pwm(pin, t):
        """ 生成 38KHz PWM 信号 """
        for i in range(t // (26 + 2)):  # +2为软PWM时间修正，下面-1同理
            # 在循环中，先将引脚输出置为高电平，保持13微秒，再将输出置为低电平，同样保持13微秒，这样就完成了一个红外信号码的发送
            wiringpi.digitalWrite(pin, wiringpi.HIGH)
            wiringpi.delayMicroseconds(13 - 1)
            wiringpi.digitalWrite(pin, wiringpi.LOW)
            wiringpi.delayMicroseconds(13 - 1)

    def send(self, pin, send_data):
        # pin参数用于指定红外传感器的引脚，而send_data参数则是包含红外数据的列表。
        # 该方法将发送红外数据，并返回发送结果。
        """ 发送红外信号 """
        
        # 初始化
        send_row = []
        # 将Raspberry Pi 的某个引脚(pin)配置为输出模式(output mode)。这意味着该引脚将被用于输出电信号，并且需要在代码中进行编程来控制输出电平（high或low）
        wiringpi.pinMode(pin, wiringpi.OUTPUT)
        wiringpi.digitalWrite(pin, wiringpi.LOW)
        time.sleep(0.1)# 让程序暂停0.1秒, (1秒 = 1000毫秒)
        
        # 生成时序编码表
        send_row.append(IR.StartLevel)  # 起始码
        send_row += [(IR.HighLevel if k == 1 else IR.LowLevel)
                     for k in send_data[0]]  # 前35位数据码
        
        send_row.append(IR.LinkLevel)  # 连接码
        send_row += [(IR.HighLevel if k == 1 else IR.LowLevel)
                     for k in send_data[1]]  # 后32位数据码
        
        # 发送信号
        # send_row : StartLevel + (35)HighLevel/LowLevel... + LinkLevel + (32)HighLevel/LowLevel...
        for signal in send_row: 
            self._pwm(pin, signal[0])
            wiringpi.delayMicroseconds(signal[1])
        self._pwm(pin, 560)

    def read(self, pin):
        """ 接收红外信号 """
        self.input_pin = pin
        wiringpi.pinMode(pin, wiringpi.INPUT)  # 设置IO输入模式
        wiringpi.pullUpDnControl(pin, wiringpi.PUD_UP)  # 设置IO上拉
        # 如果使用上拉电阻，它会将输入引脚连接到电源电压，使其在没有输入信号时维持为高电平状态
        time.sleep(0.1)
        wiringpi.wiringPiISR(pin, wiringpi.INT_EDGE_BOTH,
                             self._interrupt_handler)  # 设置中断,上升&下降
        # 这行代码是用来设置 GPIO 中断的，具体含义如下：
		# wiringpi.wiringPiISR：设置 GPIO 的中断服务程序 (Interrupt Service Routine)
		# pin：GPIO 引脚号
		# wiringpi.INT_EDGE_BOTH：设置触发中断的电平变化，这里是上升沿和下降沿都会触发中断
		# self._interrupt_handler：中断处理函数，也就是中断服务程序，用于响应 GPIO 引脚电平变化产生的中断事件
		# 在这里，当 GPIO 引脚的电平发生上升沿或下降沿时，就会触发中断，然后调用指定的中断处理函数 _interrupt_handler。这里的中断处理函数会把接收到的红外数据解码并打印出来。
        print('reading...')

    def _interrupt_handler(self):
        """ 外部中断处理函数 """
        current_time = wiringpi.micros()
        # 用于获取当前时间（以微秒为单位）的整数值
        status = wiringpi.digitalRead(self.input_pin)
        # 读取树莓派上特定引脚（self.input_pin）的数字输入状态，即读取该引脚上的电压是高电平（1）还是低电平（0）
        diff_time = current_time - self.last_time

        # 数据位
        if self.is_start and status == 0:
            if diff_time > 30000:
                self.is_start = False
                self.data_list = []
                print(f'=-=-=-=-=-= end =-=-=-=-=-=\n')
            elif self._is_equal(diff_time, 20000):  # 数据分割
                print(' '.join(self.data_list))
                # 将列表 self.data_list 中的所有元素用空格连接起来，然后输出成一个字符串。比如，如果 self.data_list 为 ['a', 'b', 'c']，则输出的结果为 "a b c"。
                self.data_list = []
                print(f'- - - - - - - - - - - - -')
            else:
                self.data_list.append(str(diff_time // 1000))
                # 如果 diff_time 的值为 5500，则 diff_time // 1000 的结果为 5。字符串类型的 str() 函数将其转换为字符串类型，最后使用 append() 方法将其添加到 self.data_list 列表的末尾。
                if len(self.data_list) == 16:
                    print(' '.join(self.data_list))
                    self.data_list = []
                    
        # 引导码
        if not self.is_start and status == 0 and self._is_equal(diff_time, 4500) and \
                self._is_equal(self.last_time - self.before_last_time, 9000):
            # \用于连接代码行
            self.is_start = True
            self.data_list = []
            print('=-=-=-=-=-= start =-=-=-=-=-=')

        # 保存上次中断时间
        self.before_last_time = self.last_time
        self.last_time = current_time


class Gree:
    """
    格力空调类
    """

    KEY_ON = 1
    KEY_OFF = 0

    MODE_AUTO = 0  # 自动
    MODE_REFRIGERATION = 1  # 制冷
    MODE_HEATING = 4  # 制热

    @staticmethod
    def _little_bit(num, digits):
        s = f'{num:0{digits}b}'[-digits:][::-1]
        return [int(b) for b in s]
   #这是一个将数字转换为二进制表示后，按照从低位到高位的顺序转换为一个列表的函数。参数num是要转换的数字，digits是转换后列表的位数。函数首先将数字转换为二进制表示，然后从低位到高位提取digits位数，再将这些位数按照从低位到高位的顺序存储在一个列表中，并返回该列表。例如，如果num是10，digits是4，那么s就是"1010"，函数将返回[0, 1, 0, 1]。

    @staticmethod
    def create_ir_data(key: int, mode: int, temp: int):
        return Gree._create_ir_data(key=key, mode=mode, temp=temp)

    @staticmethod
    def _create_ir_data(**kwargs):
        mode = kwargs.get('mode', 0)  # 模式
        key = kwargs.get('key', 0)  # 开关 0,1
        temp = kwargs.get('temp', 24) - 16  # 温度 ??? 26?
        
        verify = mode + temp - key * 8 + 10
        
        # 不校验数据合法性
        ir_data = [[[], []], [[], []]]
#[
#  [
#    [],  # 空列表	00
#    []   # 空列表 01
#  ],
#  [
#    [],  # 空列表	10
#    []   # 空列表	11
#  ]
#]
		# 前35位数据码
        ir_data[0][0] = Gree._little_bit(mode, 3)  	# 模式
        ir_data[0][0] += Gree._little_bit(key, 1)  	# 开关
        ir_data[0][0] += Gree._little_bit(0, 4)		# 风速2、扫风、睡眠
        ir_data[0][0] += Gree._little_bit(temp, 4)  # 温度
        ir_data[0][0] += [0, 0, 0, 0, 0, 0, 0, 0, 	# 定时数据
                          0, 1, 0, 0,				# 超强、灯光、健康、干燥
                          0,						# 换气
                          0, 0, 0, 1, 0, 1, 0, 0, 1, 0]#所有的按键都是这个值
        # 后32位数据码
        ir_data[0][1] = [0,			# 上下扫风
                         0, 0, 0,	# 所有按键都是这个值
                         0, 		# 左右扫风
                         0, 0, 0,	# 所有按键都是这个值
                         0, 0,		# 温度显示
                         0, 0,		# ?
                         0, 0, 0, 0,# ?
                         0, 0, 0, 0,# ?
                         0, 0, 0, 0,# ?
                         0, 0,
                         0,			# 节能
                         0]			# ?
        ir_data[0][1] += Gree._little_bit(verify, 4)  # 校验位
        
        ir_data[1][0] = ir_data[0][0].copy()
        ir_data[1][1] = ir_data[0][1].copy()
        
        ir_data[1][0][29] = 1 # ?
        
        return ir_data


def main():
    # key：开/关、mode模式：refrigeration制冷/heating制热、temp(temperature)温度
    # 开(1)，制冷(1)，26度(26)
    ir_data = Gree.create_ir_data(
        key=Gree.KEY_ON, mode=Gree.MODE_REFRIGERATION, temp=21)
    
    IR().send(1, ir_data[0])
    
    wiringpi.delayMicroseconds(40000)
    
    IR().send(1, ir_data[1])


if __name__ == '__main__':
    main()
